﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GN.Pay.Core.Entities;
using GN.Pay.Core.Values.TradeRefund;
using GN.Pay.Core.Repositories;
using GN.Pay.Core.Exceptions;
using static GN.Pay.Core.Consts.ServiceConstant;
using GN.Pay.Core.Values.TradeTransfers;
using Abp.AutoMapper;
using GN.Pay.TradeChannel.Transfers;
using GN.Pay.Core.Tasks.Queue;
using GN.Pay.TradeChannel;
using Abp.Auditing;
using GN.Pay.Core.Values;
using System.Net;
using static GN.Pay.Core.Consts;

namespace GN.Pay.Core.Services.Impl
{
    /// <summary>
    /// 交易转账申请服务
    /// </summary>
    class TradeTransfersApplyService : ATradeChannelApplyService, ITradeTransfersApplyService
    {
        private readonly ITradeTransfersApplyRepository repository;
        private readonly ITradeTransfersBookRepository bookRepository;
        private readonly ITradeCacheService tradeCacheService;
        private readonly IQueueManager queueManager;

        public IClientInfoProvider ClientInfo { get; set; }

        public TradeTransfersApplyService(ITradeTransfersApplyRepository repository,
            ITradeTransfersBookRepository bookRepository,
        ITradeCacheService tradeCacheService, Tasks.Queue.IQueueManager queueManager)
        {
            this.repository = repository;
            this.bookRepository = bookRepository;
            this.tradeCacheService = tradeCacheService;
            this.queueManager = queueManager;
            ClientInfo = Abp.Auditing.NullClientInfoProvider.Instance;
        }

        /// <summary>
        /// 第三方通道的订单号
        /// </summary>
        /// <param name="applyNo">申请号</param>
        /// <returns>返回第三方通道的订单号</returns>
        private string ChannelTransfersNo(string applyNo)
        {
            return DateTime.Now.ToString("yyyyMMddHHmmssffffff");
            //贵阳银行最长为20
            // return channelNumberSuffix + "_" + orderNo + "_" + appNumberSuffix;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="apply">申请</param>
        /// <returns></returns>
        public TradeTransfersApplyResult TransfersApply(TradeTransfersApplyInfo apply)
        {
            apply.Validate();
            apply.AppIp = ClientInfo.ClientIpAddress == null ? Start.Local_IPAddress.ToString() : ClientInfo.ClientIpAddress;
            var channel = tradeCacheService.GetTradeChannel(apply.AppId, apply.ChannelId);
            if (channel == null)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的应用对应的未配置交易通道[" + apply.ChannelId + "]");
            }
            ITransfers transfers = channel as ITransfers;
            if (transfers == null)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的交易通道不支持转账操作");
            }
            var transfersNo = ChannelTransfersNo(apply.ApplyNo);
            transfers.TransfersApplyValidate(apply.CreateTransfersApplyInfo(transfersNo));
            //已申请过，返回申请过的结果
            var result = this.GetApplyToResult(apply);
            if (result != null)
            {
                return result;
            }
            result = GetBookToResult(apply);
            if (result != null)
            {
                return result;
            }
            var transfersApply = apply.MapTo<TradeTransfersApply>();
            transfersApply.Id = transfersNo;
            transfersApply.ApplyState = TradeApplyState.Apply_Success;
            transfersApply.ApplyStateMessage = "申请成功";
            transfersApply.ApplyTime = DateTime.Now;
            transfersApply.LastSubmitTime = null;

            this.repository.Insert(transfersApply);
            repository.SaveChanges();

            var msg = new RabbitMQ.GNClient.ObjectMessage(transfersApply);
            msg.MessageId = "0";
            queueManager.Publish(QueueType.ClientTransfersApply, msg);

            result = transfersApply.MapTo<TradeTransfersApplyResult>();

            return result;
        }

        /// <summary>
        /// 获取申请转结果
        /// </summary>
        /// <param name="applyInfo"></param>
        /// <returns></returns>
        private TradeTransfersApplyResult GetApplyToResult(TradeTransfersApplyInfo applyInfo)
        {
            var apply = this.repository.FirstOrDefault(b => b.AppId == applyInfo.AppId && b.ApplyNo == applyInfo.ApplyNo);
            if (apply == null)
            {
                return null;
            }
            if (apply.ApplyMoney != applyInfo.ApplyMoney)
            {
                PayExceptionUtils.ThrowPayValidationException("相同的应用和申请号，申请金额必须一致。");
            }
            var result = apply.MapTo<TradeTransfersApplyResult>();
            result.TradeNo = apply.Id;
            return result;
        }

        /// <summary>
        /// 获取退款账本转结果
        /// </summary>
        /// <param name="applyInfo"></param>
        /// <returns></returns>
        private TradeTransfersApplyResult GetBookToResult(TradeTransfersApplyInfo applyInfo)
        {
            var book = this.bookRepository
                .FirstOrDefault(b => b.AppId == applyInfo.AppId && b.ApplyNo == applyInfo.ApplyNo);
            if (book == null)
            {
                return null;
            }
            if (book.TransfersMoney != applyInfo.ApplyMoney)
            {
                PayExceptionUtils.ThrowPayValidationException("相同的应用和申请号，申请金额必须一致。");
            }
            var result = book.MapTo<TradeTransfersApplyResult>();
            result.TradeNo = book.Id;
            result.ApplyState = TradeApplyState.Execute_Success;
            result.ApplyStateMessage = "转账成功";
            return result;
        }

        /// <summary>
        /// 转账
        /// </summary>
        /// <param name="apply">申请</param>
        /// <returns></returns>
        public TradeApplyResultState Transfers(TradeTransfersApply apply)
        {
            var channel = tradeCacheService.GetTradeChannel(apply.AppId, apply.ChannelId);
            if (channel == null)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的应用对应的未配置交易通道[" + apply.ChannelId + "]");
            }
            ITransfers transfers = channel as ITransfers;
            if (transfers == null)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的交易通道不支持转账操作");
            }
            apply = this.repository.Get(apply.Id);
            if (apply == null)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的申请对应的数据为 null");
            }
            if (apply.ApplyState == TradeApplyState.Executeing)
            {
                PayExceptionUtils.ThrowPayValidationException("指定的申请正在执行，不能再执行");
            }
            apply.LastSubmitTime = DateTime.Now;
            var applyInfo = apply.CreateTransfersApplyInfo();
            ITransfersApplyResult result;
            string opName = "转账";
            try
            {
                this.CallApply<TradeTransfersApply, string, ITradeTransfersApplyRepository>(apply, repository, opName);
                result = transfers.TransfersApply(applyInfo);
                if (result == null)
                {
                    PayExceptionUtils.ThrowPaySystemException("支付系统接口出现异常，退款申请结果为 null");
                }
                this.CallApplyResult<TradeTransfersApply, string, ITransfersApplyResult, ITradeTransfersApplyRepository>(apply,
                    result, this.repository,
                    opName, (s, r) =>
                            {
                                if (r.ResultState == TradeApplyResultState.Complete_Success)
                                {
                                    s.TransfersNotify(r);
                                }
                                else
                                {
                                    var app = tradeCacheService.GetMerchantAppInfo(apply.AppId);
                                    if (app != null && !string.IsNullOrWhiteSpace(apply.NotifyUrl))
                                    {
                                        var message = apply.MapTo<TransfersNotifyData>();
                                        message.ExtraParam = apply.ExtraParam;
                                        message.TradeNo = apply.Id;
                                        message.Success = false;
                                        message.ChannelTradeNo = "";
                                        message.StateMessage = result.ResultStateMessage;
                                        message.ChannelName = channel.ChannelName;
                                        var notify = s.PullNotify(app, TradeNotifyType.Transfers, apply.NotifyUrl, null, message);
                                    }
                                }
                            });
                return result.ResultState;
            }
            catch (Exception err)
            {
                this.Logger.Error("通道 " + channel.ChannelId + " 退款发生异常:" + err.Message + " stackTrace:" + err.StackTrace);
                apply.ApplyState = TradeApplyState.Execute_Error;
                apply.ApplyStateMessage = err.Message + " stackTrace:" + err.StackTrace;
                this.repository.Update(apply);
                return TradeApplyResultState.Apply_Error;
            }


        }
    }
}
